package com.jrsa.augmented_reality.Sun;

//Class contains most calculations
//Most are approximations. If more accurate formulas are wanted,
//they can be changed here
//Based on http://www.esrl.noaa.gov/gmd/grad/solcalc/calcdetails.html
class AstrCalc
{
    static int calcJulianDay(int day, int month, int year)
    {
        int a = (14 - month) / 12;
        int y = year + 4800 - a;
        int m = month + 12 * a - 3;
        int JDN = day + ((153 * m + 2) / 5) + 365 * y + (y / 4) - (y / 100) + (y / 400) - 32045;

        return JDN;
    }

    static double calcJulianDate(int jDay, int hour, int minute, int second)
    {
        return (double) jDay + ((hour - 12) / 24.0) + (minute / 1440.0) + (second / 86400.0);
    }

    static double calcNumDaysSinceGN(double jd)
    {
        return jd - 2451545.0;
    }

    //returns degrees
    static double calcMeanLongitudeOfSun(double jCen)
    {
        double L = 280.46646 + jCen * (36000.76983 + jCen * .0003032);
        L %= 360;
        if(L < 0)
            L += 360;

        return L;
    }

    //returns degrees
    static double calcMeanAnomalyOfSun(double jCen)
    {
        double g = 357.52911 + jCen * (35999.05029 - 0.0001537 * jCen);
        return g;
    }

    static double calcSunEccentricity(double jCen)
    {
        return 0.016708634 - jCen * (.000042037 + .0000001267 * jCen);
    }

    //returns degrees
    static double calcEclipticLongitude(double L, double g)
    {
        return L + 1.915 * Math.sin(Math.toRadians(g)) + .020 * Math.sin(Math.toRadians(2 * g));
    }

    //returns degrees
    static double calcObliquityOfEcliptic(double jCen)
    {
        return 23 + (26 + ((21.448 - jCen * (46.815 + jCen * (.00059 - jCen * .001813)))) / 60) / 60;
    }

    //returns degrees
    static double calcLongitudeOfPerhelion(double nDays)
    {
        return 282.9404 + Double.valueOf("4.70935e-5") * nDays;
    }

    static double calcObliquityCorrection(double jCen, double oblq)
    {
        return oblq + 0.00256 * Math.cos(Math.toRadians(125.04 - 1934.136 * jCen));
    }

    //returns AU (1 AU is roughly the average Earth�Sun distance)
    static double calcDistToSun(double g)
    {
        return 1.00014 - .01671 * Math.cos(Math.toRadians(g)) - .00014 * Math.cos(2 * g);
    }

    static double calcSunEqOfCtr(double g, double T)
    {
        return Math.sin(Math.toRadians(g)) * (1.914602 - T * (0.004817 + 0.000014 * T))
                + Math.sin(Math.toRadians(2 * g)) * (0.019993 - 0.000101 * T)
                + Math.sin(Math.toRadians(3 * g)) * 0.000289;
    }

    //returns degrees
    static double calcSunApparentLongitude(double tL, double jCen)
    {
        return tL - 0.00569 - 0.00478 * Math.sin(Math.toRadians(125.04 - 1934.136 * jCen));
    }

    //returns degrees
    static double calcRightAscension(double oblqCorr, double L)
    {
        return Math.toDegrees(Math.atan2(Math.cos(Math.toRadians(oblqCorr)) * Math.sin(Math.toRadians(L)), Math.cos(Math.toRadians(L))));
    }

    //returns degrees
    static double calcDeclination(double oblq, double lambda)
    {
        return Math.toDegrees(Math.asin(Math.sin(Math.toRadians(oblq)) * Math.sin(Math.toRadians(lambda))));
    }

    static double calcVarY(double oblqCorr)
    {
        return Math.tan(Math.toRadians(oblqCorr / 2)) * Math.tan(Math.toRadians(oblqCorr / 2));
    }
    //returns minutes

    static double calcEqOfTime(double varY, double ecc, double mLong, double mAnom)
    {
        return 4 * Math.toDegrees(varY * Math.sin(2 * Math.toRadians(mLong)) - 2 * ecc
                * Math.sin(Math.toRadians(mAnom)) + 4 * ecc * varY * Math.sin(Math.toRadians(mAnom))
                * Math.cos(2 * Math.toRadians(mLong)) - 0.5 * varY * varY
                * Math.sin(4 * Math.toRadians(mLong)) - 1.25 * ecc * ecc * Math.sin(2 * Math.toRadians(mAnom)));
    }

    static double calcTrueSolarTime(double currTime, double eqTime, double lgtd, int tz)
    {
        //MOD(E315*1440+V315+4*$B$3-60*$B$4,1440)
        return (currTime * 1440 + eqTime + 4 * lgtd - 60 * tz) % 1440;
    }

    static double calcSolarZenithAngle(double lha, double lat, double decl)
    {
        return Math.toDegrees(Math.acos(Math.sin(Math.toRadians(lat))
                * Math.sin(Math.toRadians(decl)) + Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(decl))
                * Math.cos(Math.toRadians(lha))));
    }

    static double calcAzimuth(double lha, double lat, double sza, double decl)
    {
        double az = 0.0;
        if(lha > 0)
        {
            az = Math.toDegrees(Math.acos(((Math.sin(Math.toRadians(lat)) * Math.cos(Math.toRadians(sza))) - Math.sin(Math.toRadians(decl)))
                    / (Math.cos(Math.toRadians(lat)) * Math.sin(Math.toRadians(sza))))) + 180;
            az %= 360.0;
        }
        else
        {
            az = 540 - Math.toDegrees(Math.acos(((Math.sin(Math.toRadians(lat)) * Math.cos(Math.toRadians(sza)))
                    - Math.sin(Math.toRadians(decl))) / (Math.cos(Math.toRadians(lat)) * Math.sin(Math.toRadians(sza)))));
            az %= 360.0;
        }
        return az;
    }

    static double calcLAST(double gast, double longitude)
    {
        return gast + longitude / 15.0;
    }

    static double calcGAST(double gmst, double daysGN, double L, double obliquity)
    {
        double omega = 125.04 - .052954 * daysGN;
        double deltaPsi = -.000319 * Math.sin(Math.toRadians(omega)) - .000024 * Math.sin(Math.toRadians(2 * L));
        double eqeq = deltaPsi * Math.cos(Math.toRadians(obliquity));

        return gmst - eqeq;
    }

    static double calcGMST(double daysGN)
    {
        return 18.697374558 + 24.06570982441908 * daysGN;
    }
}